/*
 *  Copyright (c) 2025 Huawei Device Co., Ltd.
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:hadss_geometry_transition/src/constant.dart';
import 'package:hadss_geometry_transition/src/hide_container.dart';

import '../hadss_geometry_transition.dart';

/// 路由关联两个组件，并展示动画
class GeometryTransitionRoute<T> extends ModalRoute<T> {
  final BorderRadius closedRadius;
  final BorderRadius openRadius;
  final GlobalKey<HideContainerState> hideContainerKey;
  bool useRootNavigator = false;
  final GlobalKey closedBuilderKey;
  final CloseContainerBuilder closedBuilder;
  final OpenContainerBuilder<T> openBuilder;
  final GlobalKey _openBuilderKey = GlobalKey();
  final RectTween _rectTween = RectTween();
  final BorderRadiusTween _borderRadiusTween;
  final Tween<double> _openOpacityTween;
  AnimationStatus? _lastAnimationStatus;
  AnimationStatus? _currentAnimationStatus;

  @override
  Duration transitionDuration;

  @override
  Color? get barrierColor => null;

  @override
  bool get barrierDismissible => false;

  @override
  String? get barrierLabel => null;

  @override
  bool get maintainState => true;

  @override
  bool get opaque => true;

  GeometryTransitionRoute({
    required this.closedRadius,
    required this.openRadius,
    required this.hideContainerKey,
    required this.closedBuilderKey,
    required this.closedBuilder,
    required this.openBuilder,
    required this.useRootNavigator,
    required this.transitionDuration,
    required RouteSettings? routeSettings,
  })  : _borderRadiusTween = BorderRadiusTween(
          begin: closedRadius,
          end: openRadius,
        ),
        _openOpacityTween = Tween(begin: 0.0, end: 1.0),
        super(settings: routeSettings);

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    return Align(
        alignment: Alignment.topLeft,
        child: AnimatedBuilder(
            animation: animation,
            builder: (BuildContext context, Widget? child) {
              if (animation.isCompleted) {
                return _defaultView();
              }
              return _animationView(animation);
            }));
  }

  @override
  TickerFuture didPush() {
    _takeMeasurements(navigatorContext: hideContainerKey.currentContext!);
    animation!.addStatusListener((status) {
      _lastAnimationStatus = _currentAnimationStatus;
      _currentAnimationStatus = status;
      switch (status) {
        case AnimationStatus.dismissed:
          // 动画销毁，显示
          _toggleHide(hide: false);
          break;
        case AnimationStatus.forward:
        case AnimationStatus.reverse:
          break;
        case AnimationStatus.completed:
          // 动画完成，隐藏
          _toggleHide(hide: true);
          break;
      }
    });
    return super.didPush();
  }

  @override
  bool didPop(T? result) {
    _takeMeasurements(
      navigatorContext: subtreeContext!,
      delayForSourceRoute: true,
    );
    return super.didPop(result);
  }

  @override
  void dispose() {
    if (hideContainerKey.currentState?.isVisible == false) {
      // 路由销毁，导致动画销毁的情况，需显示
      SchedulerBinding.instance.addPostFrameCallback(
          (Duration timeStamp) => _toggleHide(hide: false));
    }
    super.dispose();
  }

  void closeContainer({T? returnValue}) {
    Navigator.of(subtreeContext!).pop(returnValue);
  }

  /// 设置容器显隐
  void _toggleHide({required bool hide}) {
    if (hideContainerKey.currentState == null) {
      return;
    }
    hideContainerKey.currentState!
      ..placeholderSize = null
      ..isVisible = !hide;
  }

  /// 测量尺寸，设置 rect tween，用于尺寸动画
  void _takeMeasurements(
      {required BuildContext navigatorContext,
      bool delayForSourceRoute = false}) {
    final RenderBox navigator = Navigator.of(
      navigatorContext,
      rootNavigator: useRootNavigator,
    ).context.findRenderObject()! as RenderBox;
    assert(navigator.hasSize);
    final Size navSize = navigator.size;
    _rectTween.end = Offset.zero & navSize;
    void takeMeasurementsInSourceRoute([Duration? _]) {
      if (!navigator.attached || hideContainerKey.currentContext == null) {
        return;
      }
      _rectTween.begin = _getRect(hideContainerKey, navigator);
      hideContainerKey.currentState!.placeholderSize = _rectTween.begin!.size;
    }

    if (delayForSourceRoute) {
      SchedulerBinding.instance
          .addPostFrameCallback(takeMeasurementsInSourceRoute);
    } else {
      takeMeasurementsInSourceRoute();
    }
  }

  /// 计算尺寸区域
  Rect _getRect(GlobalKey key, RenderBox ancestor) {
    assert(key.currentContext != null);
    assert(ancestor.hasSize);
    final RenderBox render =
        key.currentContext!.findRenderObject()! as RenderBox;
    assert(render.hasSize);
    return MatrixUtils.transformRect(
      render.getTransformTo(ancestor),
      Offset.zero & render.size,
    );
  }

  /// 判断动画是否被中断
  bool get _transitionWasInterrupted {
    bool wasInProgress = false;
    bool isInProgress = false;

    switch (_currentAnimationStatus) {
      case AnimationStatus.completed:
      case AnimationStatus.dismissed:
        isInProgress = false;
        break;
      case AnimationStatus.forward:
      case AnimationStatus.reverse:
        isInProgress = true;
        break;
      default:
        break;
    }
    switch (_lastAnimationStatus) {
      case AnimationStatus.completed:
      case AnimationStatus.dismissed:
        wasInProgress = false;
        break;
      case AnimationStatus.forward:
      case AnimationStatus.reverse:
        wasInProgress = true;
        break;
      default:
        break;
    }
    return wasInProgress && isInProgress;
  }

  /// 动画完成，加载默认开启的视图
  Widget _defaultView() {
    return SizedBox.expand(
      child: Builder(
        key: _openBuilderKey,
        builder: (BuildContext context) {
          return ClipRRect(
            borderRadius: _borderRadiusTween.end!,
            child: openBuilder(context, closeContainer),
          );
        },
      ),
    );
  }

  Widget _animationView(Animation<double> animation) {
    // 创建动画
    final Animation<double> curvedAnimation = CurvedAnimation(
        parent: animation,
        curve: Constant.springCubic,
        reverseCurve: _transitionWasInterrupted ? null : Constant.springCubic);
    final Animation<double> openOpacityCurve = CurvedAnimation(
        parent: animation,
        curve: Interval(
            0,
            Constant.shortOpacityDurationMS /
                transitionDuration.inMilliseconds.toDouble(),
            curve: Constant.sharpCurve));
    final Rect rect = _rectTween.evaluate(curvedAnimation)!;
    return SizedBox.expand(
      child: Align(
        alignment: Alignment.topLeft,
        child: Transform.translate(
            offset: Offset(rect.left, rect.top),
            child: SizedBox(
              width: rect.width,
              height: rect.height,
              child: ClipRRect(
                borderRadius: _borderRadiusTween.evaluate(curvedAnimation)!,
                child: Stack(fit: StackFit.passthrough, children: <Widget>[
                  // close view
                  FittedBox(
                    fit: BoxFit.fitWidth,
                    alignment: Alignment.topLeft,
                    child: SizedBox(
                        width: _rectTween.begin!.width,
                        height: _rectTween.begin!.height,
                        child:
                            (hideContainerKey.currentState?.isInTree ?? false)
                                ? null
                                : Builder(
                                    key: closedBuilderKey,
                                    builder: (BuildContext context) {
                                      return closedBuilder(context, () {});
                                    },
                                  )),
                  ),
                  // open view
                  FittedBox(
                      fit: BoxFit.fitWidth,
                      alignment: Alignment.topLeft,
                      child: SizedBox(
                        width: _rectTween.end!.width,
                        height: _rectTween.end!.height,
                        child: FadeTransition(
                          opacity: _openOpacityTween.animate(openOpacityCurve),
                          child: Builder(
                            key: _openBuilderKey,
                            builder: (BuildContext context) {
                              return openBuilder(context, closeContainer);
                            },
                          ),
                        ),
                      )),
                ]),
              ),
            )),
      ),
    );
  }
}
